/*
 * @Copyright: Copyright (c) 2030 吴周海
 * @Author: wzh200x@126.com
 * @Date: 2025-10-16 15:04:14
 * @LastEditors: wzh200x@126.com
 * @LastEditTime: 2025-10-16 15:54:34
 * @FilePath: \gogamewebserver\goGameWebServer\internal\platform\platformWechat.go
 * @Description: 文件功能描述
 */
package platform

import (
	"bytes"
	"encoding/json"
	"fmt"
	"goGameWebServer/internal/common/config"
	"goGameWebServer/internal/common/logger"
	"goGameWebServer/internal/common/utils"
	"goGameWebServer/internal/manager/cachemanager"
	"io"
	"net/http"

	"github.com/gogf/gf/v2/util/gconv"
)

// 微信登录返回的会话信息
type WxSession struct {
	OpenID     string `json:"openid"`
	SessionKey string `json:"session_key"`
	UnionID    string `json:"unionid,omitempty"`
	ErrCode    int    `json:"errcode,omitempty"`
	ErrMsg     string `json:"errmsg,omitempty"`
}

// 微信AccessToken响应结构体
type WxAccessTokenResponse struct {
	AccessToken string `json:"access_token"`
	ExpiresIn   int    `json:"expires_in"`
	ErrCode     int    `json:"errcode,omitempty"`
	ErrMsg      string `json:"errmsg,omitempty"`
}

// 微信AccessToken结果结构体
type WxAccessTokenResult struct {
	AccessToken string `json:"accessToken"`
	ExpireTime  int64  `json:"expireTime"`
}

type PlatformWechat struct {
	PlatformBase
}

func (p *PlatformWechat) Login(code string, option *config.PlatformOption) (output map[string]interface{}, err error) {
	// 1. 通过code获取微信会话信息
	session, err := p.GetSession(option.AppId, option.AppSecret, code)
	if err != nil {
		return output, err
	}
	output = make(map[string]interface{})
	output["open_id"] = session.OpenID
	output["session_key"] = session.SessionKey
	return output, nil
}

// 通过code获取微信会话信息
func (p *PlatformWechat) GetSession(appid string, appSecret string, code string) (*WxSession, error) {
	url := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code",
		appid, appSecret, code)

	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var session WxSession
	if err := json.Unmarshal(body, &session); err != nil {
		return nil, err
	}

	if session.ErrCode != 0 {
		return nil, fmt.Errorf("微信API错误: %d %s", session.ErrCode, session.ErrMsg)
	}

	return &session, nil
}

// UpdateAccessToken 更新微信访问令牌
func (p *PlatformWechat) UpdateAccessToken(option *config.PlatformOption) (*WxAccessTokenResult, error) {
	// 构建请求URL
	url := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s",
		option.AppId, option.AppSecret)

	// 发送GET请求
	resp, err := http.Get(url)
	if err != nil {
		logger.LoggerIns().Errorf("更新微信AccessToken请求失败: %v", err)
		return nil, err
	}
	defer resp.Body.Close()

	// 读取响应体
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		logger.LoggerIns().Errorf("读取微信AccessToken响应失败: %v", err)
		return nil, err
	}

	// 解析响应体
	var response WxAccessTokenResponse
	err = json.Unmarshal(body, &response)
	if err != nil {
		logger.LoggerIns().Errorf("微信AccessToken响应体解析失败: %v", err)
		return nil, err
	}

	// 检查响应状态
	if response.ErrCode != 0 {
		logger.LoggerIns().Errorf("获取微信AccessToken失败: %d %s", response.ErrCode, response.ErrMsg)
		return nil, fmt.Errorf("获取微信AccessToken失败: %d %s", response.ErrCode, response.ErrMsg)
	}

	// 检查AccessToken是否为空
	if response.AccessToken == "" {
		logger.LoggerIns().Errorf("微信AccessToken为空")
		return nil, fmt.Errorf("微信AccessToken为空")
	}

	// 计算过期时间（当前时间 + expires_in秒）
	expireTime := utils.GetNowTimestampSecond() + int64(response.ExpiresIn)

	// 构建返回结果
	result := &WxAccessTokenResult{
		AccessToken: response.AccessToken,
		ExpireTime:  expireTime,
	}

	// 保存AccessToken到缓存
	err = p.setAccessToken(option.AppId, result.AccessToken, result.ExpireTime)
	if err != nil {
		logger.LoggerIns().Errorf("保存微信AccessToken到缓存失败: %v", err)
		return nil, err
	}

	logger.LoggerIns().Infof("微信AccessToken更新成功，AppId: %s, 过期时间: %d", option.AppId, result.ExpireTime)
	return result, nil
}

// setAccessToken 保存微信AccessToken到缓存
func (p *PlatformWechat) setAccessToken(appId, accessToken string, expireTime int64) error {
	// 构建缓存key，包含appId以便区分不同应用
	cacheKey := fmt.Sprintf("wechat:access_token:%s", appId)

	// 计算过期时间（秒）
	// expireTime是Unix时间戳，需要转换为相对于当前时间的秒数
	currentTime := utils.GetNowTimestampSecond()
	expireSeconds := int(expireTime - currentTime)

	// 如果token已经过期或即将过期（小于60秒），则不保存
	if expireSeconds <= 60 {
		logger.LoggerIns().Warnf("微信AccessToken即将过期或已过期，不保存到缓存，AppId: %s, 剩余时间: %d秒", appId, expireSeconds)
		return fmt.Errorf("微信AccessToken即将过期或已过期")
	}

	// 保存到缓存
	err := cachemanager.ManagerCache().Put(cacheKey, accessToken, expireSeconds)
	if err != nil {
		logger.LoggerIns().Errorf("保存微信AccessToken到缓存失败，AppId: %s, Error: %v", appId, err)
		return err
	}

	logger.LoggerIns().Infof("微信AccessToken已保存到缓存，AppId: %s, Key: %s, 过期时间: %d秒", appId, cacheKey, expireSeconds)
	return nil
}

// getAccessToken 从缓存获取微信AccessToken
func (p *PlatformWechat) getAccessToken(appId string) (string, error) {
	// 构建缓存key
	cacheKey := fmt.Sprintf("wechat:access_token:%s", appId)

	// 从缓存获取
	accessToken, err := cachemanager.ManagerCache().Get(cacheKey)
	if err != nil {
		logger.LoggerIns().Errorf("从缓存获取微信AccessToken失败，AppId: %s, Error: %v", appId, err)
		return "", err
	}

	if accessToken == "" {
		logger.LoggerIns().Warnf("缓存中未找到微信AccessToken，AppId: %s", appId)
		return "", fmt.Errorf("缓存中未找到微信AccessToken")
	}

	logger.LoggerIns().Infof("从缓存获取微信AccessToken成功，AppId: %s", appId)
	return accessToken, nil
}

// GetValidAccessToken 获取有效的微信AccessToken（优先从缓存获取，如果缓存中没有则重新获取）
func (p *PlatformWechat) GetValidAccessToken(option *config.PlatformOption) (string, error) {
	// 先尝试从缓存获取
	accessToken, err := p.getAccessToken(option.AppId)
	if err == nil && accessToken != "" {
		return accessToken, nil
	}

	// 缓存中没有或获取失败，重新获取AccessToken
	logger.LoggerIns().Infof("缓存中无有效微信AccessToken，重新获取，AppId: %s", option.AppId)
	result, err := p.UpdateAccessToken(option)
	if err != nil {
		return "", err
	}

	return result.AccessToken, nil
}

// QRCodeUnlimited 生成微信小程序无限制二维码
func (p *PlatformWechat) QRCodeUnlimited(params map[string]interface{}, option *config.PlatformOption) (re map[string]interface{}, err error) {
	re = make(map[string]interface{})

	// 获取参数
	scene := gconv.String(params["scene"])
	version := gconv.String(params["version"])
	width := gconv.Int(params["width"])
	// 参数验证
	if scene == "" {
		logger.LoggerIns().Errorf("生成微信二维码失败：scene参数为空")
		return re, fmt.Errorf("scene参数为空")
	}

	// 设置默认值
	if version == "" {
		version = "release" // 默认为正式版
	}
	if width <= 0 {
		width = 430 // 默认宽度
	}

	// 获取有效的AccessToken
	accessToken, err := p.GetValidAccessToken(option)
	if err != nil {
		logger.LoggerIns().Errorf("获取微信AccessToken失败: %v", err)
		return re, err
	}

	// 构建请求参数
	reqParams := map[string]interface{}{
		"scene":       scene,
		"width":       width,
		"env_version": version,
	}

	// 将参数转换为JSON
	jsonData, err := json.Marshal(reqParams)
	if err != nil {
		logger.LoggerIns().Errorf("微信二维码参数序列化失败: %v", err)
		return re, err
	}

	// 构建请求URL
	url := fmt.Sprintf("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s", accessToken)

	// 发送POST请求
	resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData))
	if err != nil {
		logger.LoggerIns().Errorf("生成微信二维码请求失败: %v", err)
		return re, err
	}
	defer resp.Body.Close()

	// 读取响应体
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		logger.LoggerIns().Errorf("读取微信二维码响应失败: %v", err)
		return re, err
	}

	// 检查响应Content-Type，判断是否为图片数据
	contentType := resp.Header.Get("Content-Type")
	if contentType == "image/jpeg" || contentType == "image/png" || bytes.HasPrefix(body, []byte{0xFF, 0xD8}) {
		// 成功返回图片数据
		re["success"] = true
		re["data"] = body
		re["content_type"] = contentType
		// logger.LoggerIns().Infof("微信二维码生成成功，AppId: %s, Scene: %s, Size: %d bytes", option.AppId, scene, len(body))
	} else {
		// 返回的是错误信息（JSON格式）
		var errorResponse map[string]interface{}
		err = json.Unmarshal(body, &errorResponse)
		if err != nil {
			logger.LoggerIns().Errorf("解析微信二维码错误响应失败: %v", err)
			re["success"] = false
			re["errmsg"] = "响应解析失败"
			re["raw_response"] = string(body)
		} else {
			errCode := gconv.Int(errorResponse["errcode"])
			errMsg := gconv.String(errorResponse["errmsg"])
			logger.LoggerIns().Errorf("生成微信二维码失败: %d %s", errCode, errMsg)
			re["success"] = false
			re["errcode"] = errCode
			re["errmsg"] = errMsg
		}
	}

	return re, nil
}

func NewPlatformWechat() *PlatformWechat {
	return &PlatformWechat{
		PlatformBase: PlatformBase{},
	}
}
